//
//  LXNetwork.swift
//  LXMoyaHelper
//
//  Created by yoctech on 2021/8/4.
//

import UIKit
import Moya

open class LXNetManager: NSObject {
        
    public static let config = LXNetManager()
    
    /// 指定Moya.Result解析成最终需要的Model的方式,可以在这里面处理数据解密等方式
    open var serializeHandle: LXMoyaResultSerializer = LXMoyaResultSerializeNormal()
    
    /// 自定义统一的全局错误处理器
    open var requestErrorHandler: LXRequestErrorHander?

    /// 指定Moya日志插件,可以自行指定日志输出框架,默认print()
    open var loggerPlugin = NetworkLoggerPlugin()
    
    /// 指定Moya请求指示器插件,如果不使用LXNet,那么需要自己重新修改插件
    open lazy var activityPlugin = NetworkActivityPlugin.init(networkActivityClosure: { [weak self] changeType, targetType in
        if let multiTargetType = targetType as? MultiTarget {
            guard let targetType = multiTargetType.target as? LXNetShowLoading, targetType.isShowLoading else { return }
        }else{
            guard let targetType = targetType as? LXNetShowLoading, targetType.isShowLoading else { return }
        }
        guard let activityHander = self?.activityHander else { return }
        switch changeType {
        case .began:
            activityHander.showLoadingHander()
        default:
            activityHander.hideLoadingHander()
        }
    })
    
    /// 指定Moya请求指示器插件,如果直接使用LXNet,可以使用这个来修改指示器
    open var activityHander: LXActivityHander?
    
    /// 指定Moya请求回调线程,默认主线程
    open var callbackQueue: DispatchQueue? = DispatchQueue.main

    
    /// 生成Moya请求管理器,管理器使用MultiTarget作为基础类型,这样即使不使用LXNetworkApi,也可以使用其他的比如枚举类型的TargetType
    public lazy var provider: MoyaProvider<MultiTarget> = {
        let obj = MoyaProvider<MultiTarget>(session: Session(configuration: sessionConfiguration, startRequestsImmediately: false), plugins:[activityPlugin, loggerPlugin])
        return obj
    }()

    public lazy var sessionConfiguration: URLSessionConfiguration = {
        let sessionConfiguration = URLSessionConfiguration.default
        sessionConfiguration.headers = .default
        sessionConfiguration.httpAdditionalHeaders = normalHeaders
        sessionConfiguration.timeoutIntervalForRequest = timeoutIntervalForRequest
        return sessionConfiguration
    }()
    
    public var timeoutIntervalForRequest: TimeInterval = 10 {
        didSet {
            sessionConfiguration.timeoutIntervalForRequest = timeoutIntervalForRequest
            provider = MoyaProvider<MultiTarget>(session: Session(configuration: sessionConfiguration, startRequestsImmediately: false), plugins:[activityPlugin, loggerPlugin])
        }
    }
    
    public var normalHeaders: [String: String] = [:] {
        didSet {
            var headers = (sessionConfiguration.httpAdditionalHeaders ?? [:])
            normalHeaders.forEach { (key, value) in
                headers[key] = value
            }
            sessionConfiguration.httpAdditionalHeaders = headers
            provider = MoyaProvider<MultiTarget>(session: Session(configuration: sessionConfiguration, startRequestsImmediately: false), plugins:[activityPlugin, loggerPlugin])
        }
    }
    
//    open var session:
    
    /// 全局域名
    open var baseUrl: String = ""
    
    
    /// 原始的请求发起方法,不做任何处理
    /// - Parameters:
    ///   - target: 遵循TargetType协议的实例
    ///   - progress: 进度回调
    ///   - completion: 完成回调
    open class func request(target: TargetType,
                       progress: ProgressBlock? = .none,
                       completion: @escaping Completion
    ) {
        
        config.provider.request(.target(target), callbackQueue: config.callbackQueue, progress: progress, completion: completion)
    }

    
    /// 使用LXNetworkApi发起请求的基础方法
    /// - Parameters:
    ///   - baseUrl: 域名,可选值,如果不传就取全局域名
    ///   - path: 路径
    ///   - method: 请求HTTPMethod
    ///   - task: Moya的Task实例,根据不同场景使用不同Task
    ///   - headers: 请求头
    ///   - progress: 进度回调
    ///   - completion: 完成回调
    open class func request(baseUrl: String? = nil,
                       path: String,
                       method: Moya.Method,
                       task: Task,
                       headers: [String : String]? = nil,
                       progress: ProgressBlock? = .none,
                       completion: @escaping Completion
    ) {
        
        guard let baseURL = URL(string: baseUrl ?? Self.config.baseUrl) else { return }
        let api = LXNet(baseURL: baseURL, path: path, method: method, task: task, headers: headers)
        request(target: api, progress:progress, completion: completion)
        Self.config.provider.request(.target(api), callbackQueue: config.callbackQueue, progress: progress, completion: completion)
    }
    
    
    /// 使用LXNetworkApi发起POST请求
    /// - Parameters:
    ///   - baseUrl: 域名,可选值,如果不传就取全局域名
    ///   - path: 路径
    ///   - jsonObject: 请求Body对象,符合JSONSerialization.isValidJSONObject的对象
    ///   - headers: 请求头
    ///   - completion: 完成回调
    open class func post(baseUrl: String? = nil,
                       path: String,
                       jsonObject: Any,
                       headers: [String : String]? = nil,
                       completion: @escaping Completion
    ) {
        guard let baseURL = URL(string: baseUrl ?? Self.config.baseUrl) else { return }
        guard JSONSerialization.isValidJSONObject(jsonObject) else { return }
        let data = try? JSONSerialization.data(withJSONObject: jsonObject, options: .fragmentsAllowed)
        let api = LXNet(baseURL: baseURL, path: path, method: .post, task: .requestData(data ?? Data()), headers: headers)
        Self.config.provider.request(.target(api), callbackQueue: config.callbackQueue, progress: nil, completion: completion)
    }
    
    /// 使用LXNetworkApi发起GET请求
    /// - Parameters:
    ///   - baseUrl: 域名,可选值,如果不传就取全局域名
    ///   - path: 路径
    ///   - params: url后面拼接的相关参数
    ///   - headers: 请求头
    ///   - completion: 完成回调
    open class func get(baseUrl: String? = nil,
                       path: String,
                       params: [String: Any]?,
                       headers: [String : String]? = nil,
                       completion: @escaping Completion
    ) {
        guard let baseURL = URL(string: baseUrl ?? Self.config.baseUrl) else { return }
        let api = LXNet(baseURL: baseURL, path: path, method: .get, task: .requestCompositeData(bodyData: Data(), urlParameters: params ?? [:]), headers: headers)
        Self.config.provider.request(.target(api), callbackQueue: config.callbackQueue, progress: nil, completion: completion)
    }
    
    /// 使用LXNetworkApi发起上传POST请求
    /// - Parameters:
    ///   - baseUrl: 域名,可选值,如果不传就取全局域名
    ///   - path: 路径
    ///   - fileDatas: 需要上传的文件数组,MultipartFormData
    ///   - params: 附带的参数
    ///   - headers: 请求头
    ///   - progress: 上传进度回调
    ///   - completion: 完成回调
    open class func upload(baseUrl: String? = nil,
                       path: String,
                       fileDatas: [MultipartFormData],
                       params: [String: Any]?,
                       headers: [String : String]? = nil,
                       progress: ProgressBlock? = .none,
                       completion: @escaping Completion
    ) {
        guard let baseURL = URL(string: baseUrl ?? Self.config.baseUrl) else { return }
        let api = LXNet(baseURL: baseURL, path: path, method: .post, task: .uploadCompositeMultipart(fileDatas, urlParameters: params ?? [:]), headers: headers)
        Self.config.provider.request(.target(api), callbackQueue: config.callbackQueue, progress: progress, completion: completion)
    }
    
    /// 使用LXNetworkApi发起下载GET请求
    /// - Parameters:
    ///   - baseUrl: 域名,可选值,如果不传就取全局域名
    ///   - path: 路径
    ///   - filePath: 保存到本地的路径,需要传入
    ///   - params: 附带的参数
    ///   - headers: 请求头
    ///   - progress: 进度回调
    ///   - completion: 完成回调
    open class func download(baseUrl: String? = nil,
                       path: String,
                       filePath: URL,
                       params: [String: Any]?,
                       headers: [String : String]? = nil,
                       progress: ProgressBlock? = .none,
                       completion: @escaping Completion
    ) {
        guard let baseURL = URL(string: baseUrl ?? Self.config.baseUrl) else { return }
        let api = LXNet(baseURL: baseURL, path: path, method: .get, task: .downloadParameters(parameters: params ?? [:], encoding: URLEncoding.httpBody, destination: { _, _ in
            return (filePath, .removePreviousFile)
        }), headers: headers)
        Self.config.provider.request(.target(api), callbackQueue: config.callbackQueue, progress: progress, completion: completion)
    }

}
